#ifndef __EPOLL_SERVER__
#define __EPOLL_SERVER__
#include "Socket.hpp"
#include "Epoller.hpp"
#include <memory>

class EpollServer
{
    static const uint16_t DEFAULT_PORT  = 9000;
    static const int NUM                = 128;
    static const int DEFAULT_ARRAY_SIZE = 1024;
public:
    EpollServer(uint16_t port = DEFAULT_PORT) : _port(port) 
    {
        try{
            // 创建Epoller
            _epoller = std::make_unique<Epoller>();
            // 初始化监听套接字
            _listenSocket = std::make_unique<Sock>();
        } catch(const std::exception& e) {
            throw;
        }
    }

    ~EpollServer()
    {
        log(INFO, "~EpollServer(), close _listenSocket now.\n");
        _listenSocket->Close();
    }

    void initServer()
    {
        // 创建监听套接字
        _listenSocket->Socket();
        // 允许套接字绑定到一个处于 TIME_WAIT 状态的端口
        int reuse = 1;
        setsockopt(_listenSocket->GetFd(), SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &reuse, sizeof(reuse));
        _listenSocket->Bind(_port);
        _listenSocket->Listen();
        log(INFO, "Create listen socket done: %d.\n", _listenSocket->GetFd());
    }

    void runServer()
    {
        // 让服务器关心监听套接字上的读事件。实际上是将监听套接字添加到红黑树上
        _epoller->epollerCtl(EPOLL_CTL_ADD, _listenSocket->GetFd(), EPOLLIN);
        struct epoll_event recEvents[NUM];
        for(; ;) {
            // 等待事件发生，如果发生了，将事件放到recEvents数组中
            int length = _epoller->epollerWait(recEvents, NUM);
            switch (length)
            {
            case 0:
                log(INFO, "Time out.\n");
                break;
            case -1:
                log(ERROR, "Epoller wait error, why: \n", strerror(errno));
                throw std::runtime_error("Epoller wait failed");
            default:
                handlerEvent(recEvents, length);
                break;
            }
        }
    } 

    // 事件派发
    void handlerEvent(struct epoll_event* recEvents, int length)
    {
        // log(INFO, "handlerEvent.\n");
        int listenSocekt = _listenSocket->GetFd();
        for(int i = 0; i < length; ++i) {
            int nowFd = recEvents[i].data.fd;
            uint32_t event = recEvents[i].events; 
            if(event & EPOLLIN) {
                // 有读事件
                if(nowFd == listenSocekt) {
                    // 是监听套接字, 创建连接
                    acceptLink();
                } else {
                    // 不是监听套接字, 读取数据
                    readData(nowFd);
                }
            } else if(event & EPOLLOUT) {
                log(INFO, "A write event is ready.\n");
            } else {
                log(INFO, "Other events ready.\n");
            }
        }
        sleep(1);
    }

    // 创建连接
    void acceptLink()
    {
        int listenSocekt = _listenSocket->GetFd();
        std::string ip;
        uint16_t port;
        int newFd = _listenSocket->Accept(&ip, &port);
        // 让epoll关心该套接字
        _epoller->epollerCtl(EPOLL_CTL_ADD, newFd, EPOLLIN);
        log(INFO, "Server get a new link: %d. Ready to read!\n", newFd);
    }

    // 读数据
    void readData(int fd)
    {
        char buff[DEFAULT_ARRAY_SIZE];
        ssize_t n = read(fd, buff, sizeof buff - 1);
        if(n > 0) {
            buff[n] = 0;
            log(INFO, "echo: %s", buff);
            // 向客户端写回
            std::string echoString = "Server has already get your string. your fd is ";
            echoString += to_string(fd);
            echoString += '\n';
            write(fd, echoString.c_str(), echoString.size());
        } else if(n == 0) {
            log(INFO, "%d fd read done.\n", fd);
            // 不需要让epoll关心了
            _epoller->epollerCtl(EPOLL_CTL_DEL, fd, 0);
            close(fd);
        } else {
            log(WARNING, "%d fd read error! errno: %d, why: %s\n", fd, errno, strerror(errno));
            _epoller->epollerCtl(EPOLL_CTL_DEL, fd, 0);
            close(fd);
        }
    }
private:
    uint16_t _port;
    std::unique_ptr<Epoller> _epoller;
    std::unique_ptr<Sock> _listenSocket;
};

#endif